#include "Graphics.h"

Graphics::Graphics(HWND hWnd, Config* config) {
	this->hWnd      = hWnd;
	this->config    = config;
    modelFactory    = new ModelFactory(config, this);
    camera          = new Camera(config);
    allTextureFaces = new vector<Shape::TextureFace*>;
    allVertices     = new vector<Shape::VERTEX>;
    vertexCounter   = 0;
    InitAreas();
    InitD3D(hWnd);

	g_spriteBatch = unique_ptr<SpriteBatch>(new SpriteBatch(devcon));

	FW1CreateFactory(FW1_VERSION, &FontFactory);
	HRESULT hResult = FontFactory->CreateFontWrapper(dev, L"Courier", &FontWrapper);
	FontFactory->Release();
}

void Graphics::InitD3D(HWND hWnd) {
    D3D_FEATURE_LEVEL featureLevels[4] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0
    };
    D3D_FEATURE_LEVEL greatestSupportedFeatureLevel;

	//Create device, device context and swap chain
	DXGI_SWAP_CHAIN_DESC scd;
	ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
	scd.BufferCount = 1;
	scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    scd.BufferDesc.Width = config->SCREENRES_HOR;
    scd.BufferDesc.Height = config->SCREENRES_VER;
	scd.BufferDesc.RefreshRate.Numerator = 60;
	scd.BufferDesc.RefreshRate.Denominator = 1;
	scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	scd.OutputWindow = hWnd;
    scd.SampleDesc.Count = config->MSAA_COUNT;
	scd.Windowed = TRUE;
	scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	
    HRESULT hr = D3D11CreateDeviceAndSwapChain(
        NULL,                       //set to NULL to select primary graphics adapter
		D3D_DRIVER_TYPE_HARDWARE,   //optional: D3D_DRIVER_TYPE_SOFTWARE
		NULL,                       //specifies software driver
		NULL,                       //device creation flags. Optional: D3D11_CREATE_DEVICE_SINGLETHREADED for better single-threaded performance
        featureLevels,              //goes through array of feature levels, selects highest level
		3,                          //# of feature levels in array of feature levels
		D3D11_SDK_VERSION,
		&scd,
		&swapchain,
		&dev,
		&greatestSupportedFeatureLevel, //greatest feature level supported by GPU
		&devcon
    );
    if(FAILED(hr)) {
        MessageBox(0, "D3D11CreateDevice Failed.", 0, 0);
        return;
    }

	//initialize IDXGIFactory object
	IDXGIDevice * DXGIDevice = nullptr;
	dev->QueryInterface(__uuidof(IDXGIDevice), (void **)&DXGIDevice);
	IDXGIAdapter1 * graphicsAdapter = nullptr;
	DXGIDevice->GetParent(__uuidof(IDXGIAdapter1), (void **)&graphicsAdapter);
	graphicsAdapter->GetParent(__uuidof(IDXGIFactory1), (void **)&DXGIFactory);

	//Use IDXGIFactory object to get the first available graphics adapter
	DXGIFactory->EnumAdapters1(0, &graphicsAdapter);

	//Fill in device description to query the adapter for its capabilities
	DXGI_ADAPTER_DESC1 deviceDesc;
	graphicsAdapter->GetDesc1(&deviceDesc);
	wstring adapterDescription = deviceDesc.Description;
	int adapterVideoRAM = deviceDesc.DedicatedVideoMemory / (1024 * 1024);
	int adapterVideoRamShared = deviceDesc.SharedSystemMemory / (1024 * 1024);
    
    //verify that the feature level does indeed support 4x MSAA
    UINT MsaaQuality;
    dev->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, scd.SampleDesc.Count, &MsaaQuality);
    assert(MsaaQuality > 0); //the quality level should support at least 1 quality level given an R8G8B8A8 output format

	//Create depth buffer
	D3D11_TEXTURE2D_DESC zBufferDescr;
	ZeroMemory(&zBufferDescr, sizeof(D3D11_TEXTURE2D_DESC));
    zBufferDescr.Width = config->SCREENRES_HOR;
    zBufferDescr.Height = config->SCREENRES_VER;
	zBufferDescr.ArraySize = 1;
	zBufferDescr.MipLevels = 1; //the depth/stencil buffer only needs 1 mipmap level
    zBufferDescr.SampleDesc.Count = config->MSAA_COUNT;
	zBufferDescr.Format = DXGI_FORMAT_D32_FLOAT;
	zBufferDescr.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	ID3D11Texture2D *zBufferTexture;
	dev->CreateTexture2D(&zBufferDescr, 0, &zBufferTexture);

	//check the # of available quality levels given front buffer format and sample count
    UINT nrOfAvailableQualityLevels;
    dev->CheckMultisampleQualityLevels(scd.BufferDesc.Format, config->MSAA_COUNT, &nrOfAvailableQualityLevels);
    //check the # of available quality levels given back buffer format and sample count
    dev->CheckMultisampleQualityLevels(zBufferDescr.Format, config->MSAA_COUNT, &nrOfAvailableQualityLevels);

	D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
	ZeroMemory(&dsvd, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
	dsvd.Format = DXGI_FORMAT_D32_FLOAT;
	dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
	dev->CreateDepthStencilView(zBufferTexture, &dsvd, &zBuffer);
	zBufferTexture->Release();

	//Create a backbuffer and set it to be the render target
	ID3D11Texture2D *pBackBuffer;
	swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
	dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
	pBackBuffer->Release();
	devcon->OMSetRenderTargets(1, &backbuffer, zBuffer); //bind back buffer and depth buffer to Output Merger stage

			//extra indented text should be active!
									//example extra indented text
			//Create D2D factory
			/*						D2D1_FACTORY_OPTIONS options;
									options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
									D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, options, &factory);*/

			//Set up the D2D render target using the back buffer
									/*IDXGISurface *dxgiBackbuffer;
									swapchain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackbuffer));
									D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
										D2D1_RENDER_TARGET_TYPE_DEFAULT,
										D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
									factory->CreateDxgiSurfaceRenderTarget(dxgiBackbuffer, props, &d2dRenderTarget);
									dxgiBackbuffer->Release();*/

			//Create the DWrite factory
									//DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(writeFactory), (IUnknown**)(&writeFactory));

			//Create the DWrite text format
			/*						writeFactory->CreateTextFormat(
										L"Arial",
										NULL,
										DWRITE_FONT_WEIGHT_NORMAL,
										DWRITE_FONT_STYLE_NORMAL,
										DWRITE_FONT_STRETCH_NORMAL,
										50,
										L"",
										&textFormat);*/

			//Create a brush
									//d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &whiteBrush);

			//Draw some text
			/*d2dRenderTarget->BeginDraw();
			const WCHAR *text = L"Hello World";
			d2dRenderTarget->DrawTextA(text, wcslen(text), textFormat, D2D1::RectF(0, 0, 800, 600), whiteBrush);
			d2dRenderTarget->EndDraw();*/

	//Create and set the viewport. Do this AFTER you create depth buffer!
	D3D11_VIEWPORT viewport;
	ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
	viewport.TopLeftX = 0;
	viewport.TopLeftY = 0;
    viewport.Width    = config->SCREENRES_HOR;
    viewport.Height   = config->SCREENRES_VER;
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;
	devcon->RSSetViewports(1, &viewport);

	InitPipeline();
	InitGraphics();
    InitRasterizerStates();
}

//source code (C) Microsoft, source: http://msdn.microsoft.com/en-us/library/windows/desktop/hh968107%28v=vs.85%29.aspx
HRESULT CompileShader(_In_ LPCWSTR srcFile, _In_ LPCSTR entryPoint, _In_ LPCSTR profile, _Outptr_ ID3DBlob** blob) {
	if (!srcFile || !entryPoint || !profile || !blob)
		return E_INVALIDARG;

	*blob = nullptr;

	UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
	flags |= D3DCOMPILE_DEBUG;
#endif

	const D3D_SHADER_MACRO defines[] =
	{
		"EXAMPLE_DEFINE", "1",
		NULL, NULL
	};

	ID3DBlob* shaderBlob = nullptr;
	ID3DBlob* errorBlob = nullptr;
	HRESULT hr = D3DCompileFromFile(srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
		entryPoint, profile,
		flags, 0, &shaderBlob, &errorBlob);
	if (FAILED(hr)) {
		if (errorBlob) {
			OutputDebugStringA((char*)errorBlob->GetBufferPointer());
			errorBlob->Release();
		}
		if (shaderBlob) {
			shaderBlob->Release();
		}
		return hr;
	}
	*blob = shaderBlob;
	return hr;
}


void Graphics::InitPipeline() {
	//D3DCreateBlob(1000000, &vsBlob);
	//HRESULT hr = CompileShader(L"shaders.hlsl", "VShader", 0/*"vs_4_0"*/, &vsBlob);
	//assert(!FAILED(hr)); //could not compile vertex shader

	/*HRESULT hr = D3DCompile(
		"shaders.hlsl",
		sizeof("shaders.hlsl"),
		NULL,
		NULL,
		NULL,
		NULL,
		"VShader",
		D3DCOMPILE_OPTIMIZATION_LEVEL3,
		0,
		&vsBlob,
		&errorBlob
		);*/

	//D3DCreateBlob(1000000, &psBlob);
	//hr = CompileShader(L"shaders.hlsl", "PShader", 0/*"vs_4_0"*/, &psBlob);
	//assert(!FAILED(hr)); //could not compile pixel shader

	/*hr = D3DCompile(
		"shaders.hlsl",
		sizeof("shaders.hlsl"),
		NULL,
		NULL,
		NULL,
		NULL,
		"PShader",
		D3DCOMPILE_OPTIMIZATION_LEVEL3,
		0,
		&psBlob,
		&errorBlob
		);*/
	
	//assert(vsBlob == NULL || psBlob == NULL); //either shader hasn't been filled in correctly

	/*dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, vsBlob);
	dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS);*/
	/*devcon->VSSetShader(pVS, 0, 0);
	devcon->PSSetShader(pPS, 0, 0);*/

	/*							HRESULT hr;
								hr = D3DCompileFromFile(L"shaders.hlsl", 0, 0, "VShader", "vs_5_0", 0, 0, &vsBlob, &errorBlob);
								hr = D3DCompileFromFile(L"shaders.hlsl", 0, 0, "PShader", "ps_5_0", 0, 0, &psBlob, &errorBlob);*/

								//devcon->VSSetShader((ID3D11VertexShader*)&vsBlob, 0, 0);
								//devcon->PSSetShader((ID3D11PixelShader*)&psBlob, 0, 0);

								////Set input element description for the vertex shader
								//D3D11_INPUT_ELEMENT_DESC ied[] = {
								//	{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
								//	{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
								//	{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0},
								//};
								//hr = dev->CreateInputLayout(ied, 3, vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &pLayout); //check input layout! It is null after executing this line!!!
								//assert(FAILED(hr)); //if FAILED(hr) == true, something has failed
								//devcon->IASetInputLayout(pLayout);

								////Create constant buffer to be used by vertex shader
								//D3D11_BUFFER_DESC bd;
								//ZeroMemory(&bd, sizeof(bd));
								//bd.Usage = D3D11_USAGE_DEFAULT;
								//bd.ByteWidth = 176; //multiple of 16 bytes
								//bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
								//dev->CreateBuffer(&bd, NULL, &pCBuffer);
								//devcon->VSSetConstantBuffers(0, 1, &pCBuffer);



	//Retrieve and compile shaders

	ID3D10Blob *VS, *PS, *error;

	HRESULT hr;
	hr = D3DCompileFromFile(L"shaders.hlsl", 0, 0, "VShader", "vs_5_0", 0, 0, &VS, &error);
	hr = D3DCompileFromFile(L"shaders.hlsl", 0, 0, "PShader", "ps_5_0", 0, 0, &PS, &error);

	dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS);
	dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS);

	devcon->VSSetShader(pVS, 0, 0);
	devcon->PSSetShader(pPS, 0, 0);

	//Set input element description for the vertex shader
	D3D11_INPUT_ELEMENT_DESC ied[] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};

	dev->CreateInputLayout(ied, 3, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout);
	devcon->IASetInputLayout(pLayout);

	//Create constant buffer to be used by vertex shader
	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd, sizeof(bd));
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = 176; //multiple of 16 bytes
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	dev->CreateBuffer(&bd, NULL, &pCBuffer);
	devcon->VSSetConstantBuffers(0, 1, &pCBuffer);
}

void Graphics::InitGraphics() {
	//Convert index vector to array
    DWORD* OurIndices = new DWORD[allTextureFaces->size() * 6];
    for(int i=0; i<(int)allTextureFaces->size(); i++)
        for(int j=0; j<6; j++)
            OurIndices[(i*6)+j] = allTextureFaces->at((int)i)->indices[j];

    //Convert vertex vector to array
    Shape::VERTEX* OurVertices = new Shape::VERTEX[allVertices->size()];
    for(UINT i=0; i<allVertices->size(); i++)
        OurVertices[i] = allVertices->at((int)i);

	//Create empty vertex buffer
	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd, sizeof(bd));
	bd.Usage = D3D11_USAGE_DYNAMIC;
	bd.ByteWidth = sizeof(Shape::VERTEX) * allVertices->size();
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	dev->CreateBuffer(&bd, NULL, &pVBuffer);

	//Copy the vertices into the buffer
	D3D11_MAPPED_SUBRESOURCE ms;
	devcon->Map(pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
	memcpy(ms.pData, OurVertices, sizeof(Shape::VERTEX) * allVertices->size());
	devcon->Unmap(pVBuffer, NULL);

	//Create empty index buffer
	bd.Usage = D3D11_USAGE_DYNAMIC;
	bd.ByteWidth = sizeof(DWORD) * allTextureFaces->size() * 6;
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	bd.MiscFlags = 0;
	dev->CreateBuffer(&bd, NULL, &pIBuffer);

	//Copy the indices into the buffer
	devcon->Map(pIBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
	memcpy(ms.pData, OurIndices, sizeof(DWORD) * allTextureFaces->size() * 6);
	devcon->Unmap(pIBuffer, NULL);

	//Create textures and materials from texture and materials names read in InitAreas()
    CreateTextures();
    CreateMaterials();
}

void Graphics::InitRasterizerStates() {
    D3D11_RASTERIZER_DESC rd;
    rd.FillMode = D3D11_FILL_SOLID;
    rd.CullMode = D3D11_CULL_BACK;
    rd.FrontCounterClockwise = FALSE;
    rd.DepthClipEnable = TRUE;
    rd.ScissorEnable = FALSE;
    rd.AntialiasedLineEnable = FALSE;
    rd.MultisampleEnable = FALSE;
    rd.DepthBias = 0;
    rd.DepthBiasClamp = 0.0f;
    rd.SlopeScaledDepthBias = 0.0f;
    dev->CreateRasterizerState(&rd, &pRasterizerStateDefault);

    rd.FillMode = D3D11_FILL_WIREFRAME;
    rd.AntialiasedLineEnable = TRUE;
    dev->CreateRasterizerState(&rd, &pRasterizerStateWireframe);
}

void Graphics::CleanD3D() {
	swapchain->SetFullscreenState(FALSE, NULL);
	pLayout->Release();
	/*pVS->Release();
	pPS->Release();*/

	//are HLSL blobs not released?!
	/*vsBlob->Release();
	psBlob->Release();*/

	pVBuffer->Release();
	pCBuffer->Release();
	pIBuffer->Release();
	swapchain->Release();
	backbuffer->Release();

	//FontWrapper->Release();

	dev->Release();
	devcon->Release();
	zBuffer->Release();
}

Camera* Graphics::GetCamera() {
	return camera;
}

/*
 * Read in the number of areas, read in that many areas by making new area objects.
 * Stop the program if the areas file cannot be found.
 */
void Graphics::InitAreas() {
	ifstream input;
    input.open((config->PathToAreas + "All_Areas").c_str());
	assert(input.is_open());
    while(!input.eof()) {
        string areaName;
        input >> areaName;
        Areas.push_back(new Area(config, modelFactory, areaName, this));
    }
    //after all areas are read in, translate, scale and rotate all models in all areas
    for(UINT i=0; i<Areas.size(); i++) {
        for(UINT j=0; j<Areas.at(i)->Models.size(); j++) {
            Areas.at(i)->Models.at(j)->Translate(XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f));
            Areas.at(i)->Models.at(j)->Scale(XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f));
            Areas.at(i)->Models.at(j)->Rotate(XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f));
        }
    }
}

/*
 * CODE TO CONVERT STRING TO WSTRING.
 * SOURCE: http://social.msdn.microsoft.com/Forums/en-US/0f749fd8-8a43-4580-b54b-fbf964d68375/convert-stdstring-to-lpcwstr-best-way-in-c
 */
std::wstring s2ws(const std::string& s) {
	int len;
	int slength = (int)s.length() + 1;
	len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
	wchar_t* buf = new wchar_t[len];
	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
	std::wstring r(buf);
	delete[] buf;
	return r;
}

void Graphics::CreateTextures() {
	allTextures = new ID3D11ShaderResourceView*[config->GetNrOfTextures()];
	ID3D11Resource *res = NULL;

    for(UINT i=0; i<(UINT)(config->GetNrOfTextures()); i++) {
		string result1 = config->PathToTextures + config->GetTexture(i);
		LPCSTR result = result1.c_str();
		
		USES_CONVERSION;
		CreateWICTextureFromFile(dev, devcon,
			A2W(result),
			&res,
			&allTextures[i],
			30);
		
		dev->CreateShaderResourceView(res, NULL, &allTextures[i]);
		
		if (dev->CreateShaderResourceView(res, NULL, &allTextures[i]) == D3D11_ERROR_FILE_NOT_FOUND) {
			CreateWICTextureFromFile(dev, devcon,
				A2W("Textures/error.png"),
				&res,
				&allTextures[i],
				30);
			//dev->CreateShaderResourceViewFromFile(dev, "Textures/error.png", NULL, NULL, &allTextures[i], NULL);
			dev->CreateShaderResourceView(res, NULL, &allTextures[i]);
		}
    }
}

void Graphics::CreateMaterials() {
    for(UINT i=0; i<(UINT)(config->GetNrOfMaterials()); i++) {
        //create material from config->materialNames.at(i). Find out how to do this!
    }
}

/*
 * Call PrepareFrameRender() whenever you switch to rendering a different area.
 */
void Graphics::PrepareFrameRender() {
    NrOfPrecedingIndicesInAllAreas = 0;
    NrOfPrecedingVerticesInAllAreas = 0;

    for(int i=0; i<config->CurrentArea; i++) {
        NrOfPrecedingIndicesInAllAreas += Areas.at(i)->GetNrOfIndices();
        NrOfPrecedingVerticesInAllAreas += Areas.at(i)->GetNrOfVertices();
    }
}

void Graphics::RenderFrame(double deltaTime, double fps) {
    CBUFFER cBuffer;
	
	//Clear window and depth/stencil buffers
	float backgroundcolor[4] = { 255.0f, 255.0f, 255.0f, 1.0f};
	devcon->ClearRenderTargetView(backbuffer, backgroundcolor);
	devcon->ClearDepthStencilView(zBuffer, D3D11_CLEAR_DEPTH, 1.0f, 0);

    //Select render mode in rasterizer stage
    if(config->RenderMode == config->USE_DEFAULT_RASTERIZER)
        devcon->RSSetState(pRasterizerStateDefault);
    else if(config->RenderMode == config->USE_WIREFRAME_RASTERIZER)
        devcon->RSSetState(pRasterizerStateWireframe);

	//Initialize constant buffer in VRAM
	UINT stride = sizeof(Shape::VERTEX);
	UINT offset = 0;
	devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);
	devcon->IASetIndexBuffer(pIBuffer, DXGI_FORMAT_R32_UINT, 0);
	devcon->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    
	//Set default texture that is to be used until a new texture is detected
    devcon->PSSetShaderResources(0, 1, &allTextures[0]);

    int nrOfIndicesInArea = Areas.at(config->CurrentArea)->GetNrOfIndices();

	/*float lightcolor[4] = {0.5f, 0.5f, 0.5f, 1.0f};
	float ambientcolor[4] = {0.2f, 0.2f, 0.2f, 1.0f };*/
	cBuffer.LightVector = XMVectorSet(0.0f, 0.0f, -5.0f, 0.0f);
	cBuffer.LightColor = XMVectorSet(0.5f, 0.5f, 0.5f, 1.0f);
	cBuffer.AmbientColor = XMVectorSet(0.2f, 0.2f, 0.2f, 1.0f);

    for(UINT i=NrOfPrecedingIndicesInAllAreas/6; i<(UINT)(NrOfPrecedingIndicesInAllAreas/6 + nrOfIndicesInArea/6); i++) {
        int TextureNumber = config->GetTextureNumber(allTextureFaces->at(i)->texture);
		devcon->PSSetShaderResources(0, 1, &allTextures[TextureNumber]);

        XMMATRIX matRotateX,      //X rotation of an object and its normals
                 matRotateY,      //Y rotation of an object and its normals
                 matRotateZ,      //Z rotation of an object and its normals
                 matTranslate,    //translation of an object
                 matScale,        //scaling of an object
                 matFinal;        //combination of all of the above

		matRotateX = XMMatrixRotationX(XMVectorGetX(allTextureFaces->at(i)->rotation));
		matRotateY = XMMatrixRotationY(XMVectorGetY(allTextureFaces->at(i)->rotation));
		matRotateZ = XMMatrixRotationZ(XMVectorGetZ(allTextureFaces->at(i)->rotation));

		matTranslate = XMMatrixTranslation(XMVectorGetX(allTextureFaces->at(i)->translation),
										   XMVectorGetY(allTextureFaces->at(i)->translation),
										   XMVectorGetZ(allTextureFaces->at(i)->translation));

		matScale = XMMatrixScaling(XMVectorGetX(allTextureFaces->at(i)->scaling),
								   XMVectorGetY(allTextureFaces->at(i)->scaling),
								   XMVectorGetZ(allTextureFaces->at(i)->scaling));

        cBuffer.Final =     matScale * matTranslate * matRotateX * matRotateY * matRotateZ * 
						    camera->View() * camera->Proj();
	    cBuffer.Rotation =  matRotateX * matRotateY * matRotateZ;

        devcon->UpdateSubresource(pCBuffer, 0, 0, &cBuffer, 0, 0);
        devcon->DrawIndexed(6, i*6, 0);
		
    }

	//Draw sprite batches
	/*CommonStates states(dev);
	g_spriteBatch->Begin(DirectX::SpriteSortMode_Deferred, nullptr, nullptr, nullptr, nullptr, nullptr);
	g_spriteBatch->Draw(allTextures[0], XMFLOAT2((FLOAT)20, (FLOAT)50));
	g_spriteBatch->Draw(allTextures[1], XMFLOAT2((FLOAT)70, (FLOAT)90));
	g_spriteBatch->End();*/

	/*d2dRenderTarget->BeginDraw();
	const WCHAR *text = L"Hello World";
	d2dRenderTarget->DrawTextA(text, wcslen(text), textFormat, D2D1::RectF(0, 0, 800, 600), whiteBrush);
	d2dRenderTarget->EndDraw();*/

	//Draw on-screen text
	/*if(config->DisplayStats) {
		DisplayStats(fps);
	}*/
	swapchain->Present(0, 0);
	camera->UpdateViewMatrix();
}

void Graphics::DisplayStats(double fps) {
	float verticalPos = 30.0f;
	RenderTextOnScreen("SCREEN STATS", verticalPos, 30.0f, 12, 0xff000000); verticalPos += 20.0f;

	verticalPos += 20.0f;

	RenderTextOnScreen("Frame cap:", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	if (config->FrameCapped) {
		RenderTextOnScreen("-  Frame cap is ON", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
		string fpsLine2 = "-  Frames per second displayed: "; fpsLine2.append(to_string(config->Target_FPS)); RenderTextOnScreen(fpsLine2, 30.0f, verticalPos, 12, 0xff777777);
	} else {
		RenderTextOnScreen("-  Frame cap is OFF", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
		string fpsLine1 = "-  Frames per second rendered: "; fpsLine1.append(to_string(fps)); RenderTextOnScreen(fpsLine1, 30.0f, verticalPos, 12, 0xff777777);
	}

	verticalPos += 20.0f;

	RenderTextOnScreen("-  Toggle frame cap: delete", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	RenderTextOnScreen("-  Increase frame cap: page up", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	RenderTextOnScreen("-  Decrease frame cap: page down", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;

	verticalPos += 20.0f;

	RenderTextOnScreen("Movement keys:", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	RenderTextOnScreen("-  Forward, backward, left and right: W S A D", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	RenderTextOnScreen("-  Up and down: Q E", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	RenderTextOnScreen("-  Increase movement speed: left shift", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;

	verticalPos += 20.0f;

	RenderTextOnScreen("Camera controls:", 30.0f, verticalPos, 12, 0xff777777);	verticalPos += 20.0f;
	RenderTextOnScreen("-  Reset camera position: 1", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	RenderTextOnScreen("-  Toggle rendering mode: 2", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
	RenderTextOnScreen("-  Toggle up/down movement with WASD: 3", 30.0f, verticalPos, 12, 0xff777777); verticalPos += 20.0f;
}

//color = aabbggrr, e.g. 0xff0099ff
void Graphics::RenderTextOnScreen(string line, float x, float y, float size, UINT32 color) {
	//to convert std::string to WCHAR*, first convert into std::wstring and then into c_str
	std::wstring wideString = std::wstring(line.begin(), line.end());
	const wchar_t* wideLine = wideString.c_str();

	//Last argument is a combination of flags
	//(for example FW1_RESTORESTATE to keep context states unchanged)
	FontWrapper->DrawString(devcon, wideLine, size,	x, y, color, FW1_RESTORESTATE);
}
